package com.polaris.live.widget

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.ValueAnimator
import android.content.Context
import android.content.res.TypedArray
import android.graphics.Rect
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.VelocityTracker
import android.view.View
import android.view.animation.DecelerateInterpolator
import android.widget.LinearLayout
import androidx.dynamicanimation.animation.FlingAnimation
import androidx.dynamicanimation.animation.FloatValueHolder
import androidx.viewpager2.widget.ViewPager2
import com.blankj.utilcode.util.ScreenUtils
import com.polaris.live.R
import com.polaris.live.common.ext.autoDp
import kotlin.math.abs

/**
 * BannerIndicatorView
 *
 * @author Created by 天晴 on 2024/12/12 17:07
 * @since 1.0.0
 **/
class BannerIndicatorView(context: Context, attrs: AttributeSet?) : LinearLayout(context, attrs) {

    private val mScreenWidth by lazy {
        ScreenUtils.getScreenWidth()
    }
    private val mScreenHeight by lazy {
        ScreenUtils.getScreenHeight()
    }
    private val defaultChildWidth: Float = (60.autoDp).toFloat()
    private val defaultChildHeight: Float = (60.autoDp).toFloat()
    private val defaultSpacing: Float = (14.autoDp).toFloat()

    private var viewPager2: ViewPager2? = null

    private var nowIndex = 0

    private var parentWidth: Int = 0   //当前控件宽
    private var parentHeight: Int = 0  //当前控件高

    private var spacing: Float = 0f  //间距
    private var distance: Float = 0f //偏移
    private var cw: Float = 0f
    private var ch: Float = 0f
    private var childScale = 0.85f //缩放

    private var reduceLeft = 0f
    private var reduceRight = 0f

    private var mainLeft = 0f
    private var mainRight = 0f

    private var maxViewWidth = 0f

    private var distanceMin = 0f //能滑动的最小值

    private val rtl by lazy {
        context.resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL
    }

    private var distanceIsInit = false

    init {
        val a: TypedArray = context.obtainStyledAttributes(attrs, R.styleable.BannerIndicatorView, 0, 0)
        cw = a.getDimension(R.styleable.BannerIndicatorView_banner_indicator_child_width, defaultChildWidth)
        ch = a.getDimension(R.styleable.BannerIndicatorView_banner_indicator_child_height, defaultChildHeight)
        spacing = a.getDimension(R.styleable.BannerIndicatorView_banner_indicator_spacing, defaultSpacing)
        childScale = a.getFloat(R.styleable.BannerIndicatorView_banner_indicator_scale, 0.85f)
        a.recycle()
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        if (parentWidth == 0 || parentHeight == 0) {
            val widthMode = MeasureSpec.getMode(widthMeasureSpec)
            val widthSize = MeasureSpec.getSize(widthMeasureSpec)
            val heightMode = MeasureSpec.getMode(heightMeasureSpec)
            val heightSize = MeasureSpec.getSize(heightMeasureSpec)
            parentWidth = mScreenWidth
            parentHeight = mScreenHeight
            when (widthMode) {
                MeasureSpec.EXACTLY -> parentWidth = widthSize
                MeasureSpec.AT_MOST -> parentWidth = parentWidth.coerceAtMost(widthSize)
                MeasureSpec.UNSPECIFIED -> parentWidth = mScreenWidth
            }
            when (heightMode) {
                MeasureSpec.EXACTLY -> parentHeight = heightSize
                MeasureSpec.AT_MOST -> parentHeight = parentHeight.coerceAtMost(heightSize)
                MeasureSpec.UNSPECIFIED -> parentHeight = mScreenHeight
            }
            mainLeft = (parentWidth - cw) / 2f
            mainRight = mainLeft + cw
            reduceLeft = mainLeft - spacing
            reduceRight = mainRight + spacing
            if (maxViewWidth != 0f) {
                distanceMin = mainRight - maxViewWidth - mainLeft
                if (rtl && !distanceIsInit) {
                    distance = distanceMin
                    distanceIsInit = true
                }
            }

            setMeasuredDimension(parentWidth, parentHeight)
        }
    }

    fun setData(list: List<Any>) {
        removeAllViews()
        maxViewWidth = (list.size - 1) * spacing + list.size * cw
        if (distanceMin == 0f && mainRight != 0f && mainLeft != 0f) {
            distanceMin = mainRight - maxViewWidth - mainLeft
            if (rtl && !distanceIsInit) {
                distance = distanceMin
                distanceIsInit = true
            }
        }
        if (rtl) {
            for (index in list.size - 1 downTo 0) {
                createView(index, list[index])?.let {
                    addView(it)
                }
            }
        } else {
            list.forEachIndexed { index, s ->
                createView(index, s)?.let {
                    addView(it)
                }
            }
        }
    }

    private var handView: ((t: Any) -> View)? = null

    fun <T> bindChildView(createView: (t: T) -> View) {
        this.handView = { t -> createView(t as T) }
    }

    private fun createView(index: Int, data: Any): View? {
        return handView?.invoke(data)?.apply {
            val lp = LayoutParams(cw.toInt(), cw.toInt())
            layoutParams = lp
            tag = index
        }
    }

    override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
        super.onLayout(changed, l, t, r, b)
        val parentCenterX = (r - l) / 2
        var lastLeft = mainLeft + distance
        for (i in 0 until childCount) {
            val child = getChildAt(i)
            val right = lastLeft + child.width

            child.layout(lastLeft.toInt(), child.top, right.toInt(), child.bottom)

            val childCenterX = lastLeft + child.width / 2

            val distanceToCenter = abs(childCenterX - parentCenterX)

            val scale =
                (1.0f - (distanceToCenter / (cw + spacing)).coerceIn(0f, 1f)) * (1f - childScale) + childScale
            val alpha =
                (1.0f - (distanceToCenter / (cw + spacing)).coerceIn(0f, 1f)) * (1f - 0.5f) + 0.5f
            child.scaleX = scale
            child.scaleY = scale
            child.alpha = alpha
            lastLeft = right + spacing
        }
    }

    private var lastPositionOffset = -1f
    private var lastPosition = 0
    private var isDRAGGING = false
    fun bindViewPager(viewPager2: ViewPager2) {
        this.viewPager2 = viewPager2
        viewPager2.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
            override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
                super.onPageScrolled(position, positionOffset, positionOffsetPixels)
                if (!isDRAGGING) {
                    return
                }
                // 页面滑动时调用 position 是当前页面的索引
                if (lastPosition != position) {
                    lastPositionOffset = -1f
                    lastPosition = position
                }
                if (positionOffset != 0f) {
                    val p = (cw + spacing) * positionOffset
                    if (lastPositionOffset != -1f) {
                        var di = if (rtl) {
                            distance + (p - lastPositionOffset)
                        } else {
                            distance - (p - lastPositionOffset)
                        }
                        if (di > 0f) {
                            di = 0f
                        }
                        if (di < distanceMin) {
                            di = distanceMin
                        }
                        updateDistance(di)
                    }
                    lastPositionOffset = p
                }
                if ((positionOffset == 0f && positionOffsetPixels == 0)) {
                    lastPositionOffset = -1f
                    scrollPosition(lastPosition, true)
                }
            }

            override fun onPageScrollStateChanged(state: Int) {
                super.onPageScrollStateChanged(state)
                // 滑动状态发生变化时调用
                when (state) {
                    ViewPager2.SCROLL_STATE_IDLE -> {
                        isDRAGGING = false

                    }

                    ViewPager2.SCROLL_STATE_DRAGGING -> {
                        isDRAGGING = true
                    }
                }
            }
        })

    }

    private var x = 0f
    private var sx = 0f
    private var sy = 0f
    private var downTime = 0L
    private var velocityTracker: VelocityTracker? = null
    private var isLeft = false
    override fun onTouchEvent(event: MotionEvent): Boolean {
        if (maxViewWidth == 0f) {
            return super.onTouchEvent(event)
        }
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                x = event.x
                sx = event.x
                sy = event.y
                downTime = System.currentTimeMillis()
                velocityTracker = VelocityTracker.obtain()
                velocityTracker?.addMovement(event)
                flingAnimation?.cancel()
                flingAnimation = null
                valueAnimatorCancel()
            }

            MotionEvent.ACTION_MOVE -> {
                isLeft = event.x < x
                velocityTracker?.addMovement(event)
                var di = distance + (event.x - x)
                if (di > 0f) {
                    di = 0f
                }
                if (di < distanceMin) {
                    di = distanceMin
                }
                updateDistance(di)
                x = event.x
            }

            MotionEvent.ACTION_UP -> {
                if (abs(event.x - sx) < 10 &&
                    abs(event.y - sy) < 10 &&
                    System.currentTimeMillis() - downTime < 100
                ) { //是点击事件
                    for (i in 0 until childCount) {
                        val child = getChildAt(i)
                        if (isViewClicked(child, event.rawX, event.rawY)) {
                            val index = child.tag as Int
                            scrollPosition(index, true)
                            viewPager2?.setCurrentItem(index, true)
                            break
                        }
                    }
                } else {
                    velocityTracker?.computeCurrentVelocity(1000) // 单位：像素/秒
                    val velocityX = velocityTracker?.xVelocity ?: 0f
                    if (abs(velocityX) > 1000) { // 如果速度足够大
                        calculateFlingValue(velocityX)
                    } else {
                        scrollPosition(nowIndex, true)
                    }
                }

                velocityTracker?.recycle()
                velocityTracker = null
            }

            MotionEvent.ACTION_CANCEL -> {
                velocityTracker?.recycle()
                velocityTracker = null

            }

        }

        return true
    }

    /**
     * 判断是否在点击区域
     */
    private fun isViewClicked(view: View, x: Float, y: Float): Boolean {
        val location = IntArray(2)
        view.getLocationOnScreen(location)
        val rect = Rect(location[0], location[1], location[0] + view.width, location[1] + view.height)
        return rect.contains(x.toInt(), y.toInt())
    }

    private fun getPosition(): Int {
        val di = abs(distance)
        if (rtl) {
            val space = abs(distanceMin + di)
            var po = (space / (cw + spacing)).toInt()
            val po2 = space % (cw + spacing)
            if (po2 > (cw + spacing) / 2) {
                po += 1
            }
            return po
        } else {
            var po = (di / (cw + spacing)).toInt()
            val po2 = di % (cw + spacing)
            if (po2 > (cw + spacing) / 2) {
                po += 1
            }
            return po
        }
    }

    fun setPosition(position: Int, animator: Boolean) {
        post {
            scrollPosition(position, animator)
        }

    }

    /**
     * 滑动到指定位置
     *
     * animator 是否有动画
     */
    private fun scrollPosition(position: Int, animator: Boolean) {
        var np = if (rtl) {
            childCount - position - 1
        } else {
            position
        }
        getChildAt(np)?.let {
            val parentCenterX = parentWidth / 2
            val childCenterX = it.left + it.width / 2
            val distanceToCenter = childCenterX - parentCenterX
            var di = distance - distanceToCenter
            if (di > 0f) {
                di = 0f
            }
            if (di < distanceMin) {
                di = distanceMin
            }

            if (animator) {
                correctionAnimation(di)
            } else {
                updateDistance(di)
            }

        }
    }

    private var mValueAnimator: ValueAnimator? = null

    /**
     * 纠偏动画
     */
    private fun correctionAnimation(di: Float) {
        mValueAnimator = ValueAnimator.ofFloat(distance, di).apply {
            duration = 200
            interpolator = DecelerateInterpolator()
            addUpdateListener { animation ->
                val animatedValue = animation.animatedValue as Float
                updateDistance(animatedValue)
            }
            addListener(object : AnimatorListenerAdapter() {
                override fun onAnimationEnd(animation: Animator) {
                    super.onAnimationEnd(animation)
                    valueAnimatorCancel()
                }
            })
            start()
        }
    }

    private fun valueAnimatorCancel() {
        mValueAnimator?.cancel()
        mValueAnimator = null
    }

    /**
     * 惯性动画
     */
    private var flingAnimation: FlingAnimation? = null
    private fun calculateFlingValue(initialVelocity: Float) {
        val valueHolder = FloatValueHolder()
        flingAnimation = FlingAnimation(valueHolder)
        flingAnimation?.setStartVelocity(initialVelocity)
        if (initialVelocity > 0f) {
            flingAnimation?.setMinValue(0f)
            flingAnimation?.setMaxValue(abs(distance))
        } else {
            val max = distance - distanceMin
            flingAnimation?.setMinValue(-max)
            flingAnimation?.setMaxValue(0f)
        }
        flingAnimation?.friction = 1.2f
        var last = 0f
        flingAnimation?.addUpdateListener { _, value, _ ->
            updateDistance(distance - (last - value))
            last = value

        }
        flingAnimation?.addEndListener { _, _, _, _ ->
            scrollPosition(nowIndex, true)
            flingAnimation = null
        }
        flingAnimation?.start()
    }

    /**
     * 更新位置
     */
    private fun updateDistance(e: Float) {
        distance = e
        val position = getPosition()
        if (nowIndex != position) {
            if (!isDRAGGING) {
                viewPager2?.setCurrentItem(position, true)
            }
            nowIndex = position
        }
        if (isAttachedToWindow) {
            requestLayout()
        }

    }

    override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        flingAnimation?.cancel()
        flingAnimation = null
        valueAnimatorCancel()
    }
}